package com.yuncheng.sms.service;

import cn.hutool.http.HtmlUtil;
import com.yuncheng.common.utils.IPUtils;
import com.yuncheng.sms.utils.LoginSysConfig;
import com.yuncheng.system.sysconfig.service.ISysConfigService;
import com.yuncheng.util.SignUtil;
import com.yuncheng.utils.RedisUtil;
import com.yuncheng.vo.HttpResult;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * ApiSignManager
 *
 * @date 2023/5/10
 */
@Component
public class ApiSignManager implements InitializingBean {

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private ISysConfigService sysConfigService;

    @Value("${need_smscode_server_ips:}")
    private String[] needSmscodeServerIps;

    private String[] needSmscodeServerIpsFromSysConfig;

    @Value("#{${api_sign_idsecret_map:{\"\":\"\"}}}")
    private Map<String, String> appIdSecretMap;

    @Value("${api_sign_ipprefix_whitelist:}")
    private String[] ipWhiteList;

    public boolean isNeedSmscodeServer(String ipAddr) {
        return checkNeedSmscodeServer(ipAddr) || checkNeedSmscodeServerFromSysConfig(ipAddr);
    }

    private boolean checkNeedSmscodeServer(String ipAddr) {
        if (null != needSmscodeServerIps && needSmscodeServerIps.length > 0) {
            for (String p : needSmscodeServerIps) {
                if (StringUtils.hasText(p) && StringUtils.startsWithIgnoreCase(ipAddr, p)) {
                    // ip匹配需要发送验证码的内网ip名单，返回true
                    return true;
                }
            }
            // ip不匹配需要发送验证码的内网ip名单，返回false
            return false;
        } else {
            //没有设置需要发送验证码的内网ip名单，所有ip都不是
            return false;
        }
    }

    private boolean checkNeedSmscodeServerFromSysConfig(String ipAddr) {
        if (null != needSmscodeServerIpsFromSysConfig && needSmscodeServerIpsFromSysConfig.length > 0) {
            for (String p : needSmscodeServerIpsFromSysConfig) {
                if (StringUtils.hasText(p) && StringUtils.startsWithIgnoreCase(ipAddr, p)) {
                    // ip匹配需要发送验证码的内网ip名单，返回true
                    return true;
                }
            }
            // ip不匹配需要发送验证码的内网ip名单，返回false
            return false;
        } else {
            //没有设置需要发送验证码的内网ip名单，所有ip都不是
            return false;
        }
    }

    public void refreshNeedSmscodeServerIpsFromSysConfig() {
        String result = sysConfigService.querySysConfigByCode(LoginSysConfig.LOGIN_NEED_SMSCODE_SERVER_IPS.getConfigCode());
        if (StringUtils.hasText(result)) {
            this.needSmscodeServerIpsFromSysConfig = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(result));
        } else {
            this.needSmscodeServerIpsFromSysConfig = null;
        }
    }

    private String getSecretByAppId(String appId) {
        return appIdSecretMap.get(appId);
    }

    private boolean checkIpInWhile(String ipAddr) {
        if (null != ipWhiteList && ipWhiteList.length > 0) {
            for (String p : ipWhiteList) {
                if (StringUtils.startsWithIgnoreCase(ipAddr, p)) {
                    // ip匹配白名单，返回true
                    return true;
                }
            }
            // ip不匹配白名单，返回false
            return false;
        } else {
            //没有设置ip白名单，所有ip都允许
            return true;
        }
    }

    private Map<String, String> getApiSignParams(HttpServletRequest request, String... params) {
        Map<String, String> result = new HashMap<>();
        if (null != params) {
            for (String p : params) {
                result.put(p, request.getParameter(p));
            }
        }
        return result;
    }

    /**
     * @param paramData
     * @param params
     * @return
     */
    private Map<String, String> getApiSignParams2(Map<String, String> paramData, String... params) {
        Map<String, String> result = new HashMap<>();
        if (null != params) {
            for (String p : params) {
                result.put(p, paramData.get(p));
            }
        }
        return result;
    }

    private String getNonce(Map<String, String> params) {
        String nonce = params.get(SignUtil.NONCE_KEY);
        if (StringUtils.hasText(nonce)) {
            return nonce + "_" + params.get(SignUtil.SIGN_KEY);
        }
        return null;
    }

    public HttpResult<?> checkApiSign(HttpServletRequest request, String... params) {
        String ipAddr = IPUtils.getIpAddr(request);
        if (checkIpInWhile(ipAddr)) {
            Map<String, String> result = getApiSignParams(request, params);
            result.putAll(getApiSignParams(request, SignUtil.SIGN_KEY, SignUtil.APPID_KEY, SignUtil.NONCE_KEY, SignUtil.TIMESTAMP_KEY));
            if (SignUtil.validateTimestamp(result.get(SignUtil.TIMESTAMP_KEY), SignUtil.DEFAULT_TIMESTAMP_TIMEOUT_MS)) {
                String nonceKey = getNonce(result);
                if (StringUtils.hasText(nonceKey) && redisUtil.hasKey(nonceKey)) {
                    // 检查到重放参数签名的调用请求，拒绝执行
                    return HttpResult.error(401, "参数签名的请求已执行过！");
                }
                if (StringUtils.hasText(nonceKey) &&
                        SignUtil.validateSign(result, getSecretByAppId(result.get(SignUtil.APPID_KEY)))) {
                    // 校验成功，用两倍超时阈值秒数作为防重放超时时间
                    redisUtil.set(nonceKey, "1", SignUtil.DEFAULT_TIMESTAMP_TIMEOUT_MS / 1000 * 2);
                    return HttpResult.ok();
                } else {
                    return HttpResult.error(401, "参数签名校验失败！");
                }
            } else {
                return HttpResult.error(401, "时间戳参数超时！");
            }
        } else {
            return HttpResult.error(401, "客户端IP不在白名单中！");
        }
    }

    public HttpResult<?> checkSignBySecret(String secret,HttpServletRequest request, Map<String, String> paramData, String... params) {
        String ipAddr = IPUtils.getIpAddr(request);
        if (checkIpInWhile(ipAddr)) {
            Map<String, String> result = getApiSignParams2(paramData, params);
            result.putAll(getApiSignParams2(paramData, SignUtil.SIGN_KEY, SignUtil.APPID_KEY, SignUtil.NONCE_KEY, SignUtil.TIMESTAMP_KEY));
            if (SignUtil.validateTimestamp(result.get(SignUtil.TIMESTAMP_KEY), SignUtil.DEFAULT_TIMESTAMP_TIMEOUT_MS)) {
                String nonceKey = getNonce(result);
                if (StringUtils.hasText(nonceKey) && redisUtil.hasKey(nonceKey)) {
                    // 检查到重放参数签名的调用请求，拒绝执行
                    return HttpResult.error(401, "参数签名的请求已执行过！");
                }
                if (StringUtils.hasText(nonceKey) &&
                        SignUtil.validateSign(result, secret)) {
                    // 校验成功，用两倍超时阈值秒数作为防重放超时时间
                    redisUtil.set(nonceKey, "1", SignUtil.DEFAULT_TIMESTAMP_TIMEOUT_MS / 1000 * 2);
                    return HttpResult.ok();
                } else {
                    return HttpResult.error(401, "参数签名校验失败！");
                }
            } else {
                return HttpResult.error(401, "时间戳参数超时！");
            }
        } else {
            return HttpResult.error(401, "客户端IP不在白名单中！");
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        refreshNeedSmscodeServerIpsFromSysConfig();
        if (null != needSmscodeServerIps) {
            needSmscodeServerIps = StringUtils.trimArrayElements(needSmscodeServerIps);
        }
    }
}
